package com.basker.pisces.domain.property.meta;

import java.util.Arrays;

import org.springframework.util.StringUtils;

import com.basker.pisces.core.meta.IDataField;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.annotation.DataField;
import com.basker.pisces.core.utils.ConvertUtils;
import com.basker.pisces.domain.ISupportEntityBuild;
import com.basker.pisces.domain.constant.DomainConstants.ObjectErrorCode;
import com.basker.pisces.domain.entity.meta.Entity;
import com.basker.pisces.domain.property.InvalidPropertyValueException;
import com.basker.pisces.domain.value.DurationValue;
import com.basker.pisces.domain.value.DurationValue.DurationUnit;
import com.basker.pisces.resource.StringResources;
import com.basker.pisces.service.exception.IObjectErrorsWrapper.ObjectError;
import com.basker.pisces.utils.NumberUtils;

/**
 * 维护时间长度的属性类型
 *
 * @author hangwen
 */
public class DurationProperty extends Property<DurationValue> implements ISupportEntityBuild {

    @DataField
    private String unitPattern;

    private transient DurationUnit unit;
    private transient Class<?> valueType;

    public DurationProperty() {
        this.setUnit(DurationUnit.Day);
    }

    @Override
    public void bindField(IDataObjectMeta objectMeta) {
        super.bindField(objectMeta);

        if (this.field instanceof IDataField) {
            this.valueType = ((IDataField) this.field).getFieldClass();
            return;
        }

        throw new RuntimeException("field:'" + this.field.getName() + "' must be DataField");
    }

    @Override
    public void build(Entity entity) {
        if (this.unit != null) {
            return;
        }

        if (StringUtils.isEmpty(this.unitPattern)) {
            this.setUnit(DurationUnit.Day);
        } else {
            this.setUnit(DurationUnit.parse(this.unitPattern));
        }
    }

    @Override
    public DurationValue createValue(Object rawValue) {
        return this.convertToDuration(rawValue);
    }

    public DurationUnit getUnit() {
        return unit;
    }

    public String getUnitPattern() {
        return unitPattern;
    }

    public void setUnit(DurationUnit unit) {
        this.unit = unit;
        this.unitPattern = unit.getPattern();
    }

    public void setUnitPattern(String unitPattern) {
        this.unitPattern = unitPattern;
    }

    /**
     * inputValue可以是一个DurationValue实例，否则录入值类型必须和属性内部映射的类型一致
     */
    @Override
    public void setValue(Object dataObject, Object inputValue) throws InvalidPropertyValueException {
        if (inputValue == null) {
            this.setRawValue(dataObject, inputValue);
        }

        DurationValue duration = null;
        try {
            duration = this.convertToDuration(inputValue);
        } catch (IllegalArgumentException e) {
            ObjectError error = new ObjectError(ObjectErrorCode.DURATION_INVALID, dataObject, null,
                    this.getInvalidInputValueMessage(this.toString(), inputValue));
            throw new InvalidPropertyValueException(Arrays.asList(error));
        }

        if (NumberUtils.isNumberClass(this.valueType)) {
            this.setRawValue(dataObject, ConvertUtils.convert(duration.getTime(), this.valueType));
        } else {
            this.setRawValue(dataObject, duration.getPresentValue());
        }
    }

    @Override
    protected String getDefaultInvalidInputValueMessage(String propertyDesc, Object inputValue) {
        return StringResources.get("pisces-domain.DurationProperty.InvalidValue", propertyDesc, inputValue);
    }

    private DurationValue convertToDuration(Object rawValue) {
        if (StringUtils.isEmpty(rawValue)) {
            return DurationValue.NULL_Duration;
        }

        if (rawValue instanceof DurationValue) {
            return (DurationValue) rawValue;
        }

        if (rawValue instanceof Number) {
            return new DurationValue(((Number) rawValue).intValue(), this.unit);
        }

        if (rawValue instanceof String) {
            return DurationValue.parse(rawValue.toString());
        }

        throw new IllegalArgumentException("paramter 'rawValue':" + rawValue + " can not be convert to DurationValue");
    }

}
